// author: Shelman
// description: 封装的使用curl接口实现的FTP客户端

#include "FtpClient.h"


// 下载数据回调函数
static size_t pfnWriteCallback(void* contents, size_t size, size_t nmemb, FILE* stream)
{
    return fwrite(contents, size, nmemb, stream);
}

// 上传数据回调函数
static size_t pfnReadCallback(void* ptr, size_t size, size_t nmemb, FILE* stream)
{
    return fread(ptr, size, nmemb, stream);
}


/*****************************************************
 * 功能：构造函数
 * ***************************************************/
FtpClient::FtpClient()
{
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl_ = curl_easy_init();
}

/*****************************************************
 * 功能：析构函数
 * ***************************************************/
FtpClient::~FtpClient()
{
    if (curl_) {
        curl_easy_cleanup(curl_);
        curl_ = nullptr;
    }
    curl_global_cleanup();
}

/*****************************************************
 * 功能：上传文件到FTP服务器
 * 输入参数：
 *      server：FTP服务端信息，url或者ip:port
 *      username：连接FTP服务的用户名
 *      password：连接FTP服务的密码
 *      localFilePath：本地文件路径
 *      remoteFilePath：远程文件路径
 * 返回值：
 *      bool：调用结果，true -- 成功，false -- 失败
 * ***************************************************/
bool FtpClient::uploadFile(const std::string &server, const std::string &username
                           , const std::string &password, const std::string &localFilePath
                           , const std::string &remoteFilePath)
{
    if (!curl_) {
        std::cerr << "Curl initialization failed!" << std::endl;
        return false;
    }

    // 构造FTP URL
    std::string url = "ftp://" + server + "/" + remoteFilePath;
    curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());

    // 设置用户名和密码
    std::string userpwd = username + ":" + password;
    curl_easy_setopt(curl_, CURLOPT_USERPWD, userpwd.c_str());

    // 打开本地文件
    FILE* file = fopen(localFilePath.c_str(), "rb");
    if (!file) {
        std::cerr << "Failed to open local file: " << localFilePath << std::endl;
        return false;
    }

    // 获取文件大小
    fseek(file, 0, SEEK_END);
    long fileSize = ftell(file);
    fseek(file, 0, SEEK_SET);

    // 设置上传选项
    curl_easy_setopt(curl_, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl_, CURLOPT_READFUNCTION, pfnReadCallback);
    curl_easy_setopt(curl_, CURLOPT_READDATA, file);
    curl_easy_setopt(curl_, CURLOPT_INFILESIZE, fileSize);

    // 自动创建缺失的目录
    curl_easy_setopt(curl_, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);

    // 执行上传
    res_ = curl_easy_perform(curl_);
    fclose(file);

    if (res_ != CURLE_OK) {
        std::cerr << "Upload failed: " << curl_easy_strerror(res_) << std::endl;
        return false;
    }

    std::cout << "File uploaded successfully!" << std::endl;
    return true;
}

/*****************************************************
 * 功能：从FTP服务器下载文件
 * 输入参数：
 *      server：FTP服务端信息，url或者ip:port
 *      username：连接FTP服务的用户名
 *      password：连接FTP服务的密码
 *      remoteFilePath：远程文件路径
 *      localFilePath：本地文件路径
 * 返回值：
 *      bool：调用结果，true -- 成功，false -- 失败
 * ***************************************************/
bool FtpClient::downloadFile(const std::string &server, const std::string &username
                             , const std::string &password, const std::string &remoteFilePath
                             , const std::string &localFilePath)
{
    if (!curl_) {
        std::cerr << "Curl initialization failed!" << std::endl;
        return false;
    }

    // 构造FTP URL
    std::string url = "ftp://" + server + "/" + remoteFilePath;
    curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());

    // 设置用户名和密码
    std::string userpwd = username + ":" + password;
    curl_easy_setopt(curl_, CURLOPT_USERPWD, userpwd.c_str());

    // 创建本地文件
    FILE* file = fopen(localFilePath.c_str(), "wb");
    if (!file) {
        std::cerr << "Failed to create local file: " << localFilePath << std::endl;
        return false;
    }

    // 设置下载选项
    curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, pfnWriteCallback);
    curl_easy_setopt(curl_, CURLOPT_WRITEDATA, file);

    // 执行下载
    res_ = curl_easy_perform(curl_);
    fclose(file);

    if (res_ != CURLE_OK) {
        std::cerr << "Download failed: " << curl_easy_strerror(res_) << std::endl;
        return false;
    }

    std::cout << "File downloaded successfully!" << std::endl;
    return true;
}

